package org.common.dynamicbean;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import org.common.utils.DeepCopy;

/**
 *
 * @author gus
 */
public class DynamicBean implements DynamicPropertySet, Cloneable
{
    private static final long serialVersionUID = -3647925080211614164L;
    private PropertyChangeSupport propertyChangeSupport = null;
    protected Map<String, DynamicProperty> propertiesMap;

    public DynamicBean()
    {
        propertiesMap = new TreeMap<String, DynamicProperty>();
    }

    @Override
    public boolean contains(DynamicProperty property)
    {
        return propertiesMap.containsValue(property);
    }

    protected DynamicProperty findProperty(String name)
    {
        return propertiesMap.get(name);
    }

    @Override
    public DynamicProperty getProperty(String name)
    {
        return findProperty(name);
    }

    /** @return type for the property with specified name. */
    @Override
    public Class getType(String name)
    {
        DynamicProperty property = findProperty(name);
        return property != null ? property.getType() : null;
    }

    @Override
    public <T> T getValue(String name)
    {
        DynamicProperty property = findProperty(name);
        return (T) (property != null ? property.getValue() : null);
    }

    @Override
    public Iterator<String> nameIterator()
    {
        return propertiesMap.keySet().iterator();
    }

    @Override
    public Map<String, DynamicProperty> asMap()
    {
        return Collections.unmodifiableMap(propertiesMap);
    }

    @Override
    public void setValue(String name, Object value)
    {
        DynamicProperty property = findProperty(name);
        if (property == null)
        {
            throw new IllegalArgumentException("Could not find property " + name + " in dynamic property set.");
        }

        Object oldValue = property.getValue();
        property.setValue(value);
        if (hasListeners(name))
        {
            firePropertyChange(new PropertyChangeEvent(this, name, oldValue, value));
        }
    }

    @Override
    public void add(DynamicProperty property)
    {
        propertiesMap.put(property.getName(), property);
        property.setParent(this);
    }

    @Override
    public boolean remove(String name)
    {
        if (propertiesMap.containsKey(name))
        {
            DynamicProperty property = propertiesMap.get(name);
            property.setParent(null);
            propertiesMap.remove(name);

            return true;
        }

        return false;
    }

    @Override
    public boolean replaceWith(String name, DynamicProperty prop)
    {
        if (remove(name))
        {
            add(prop);
            return true;
        }

        return false;
    }

    @Override
    public void addPropertyChangeListener(PropertyChangeListener listener)
    {
        if (propertyChangeSupport == null)
        {
            propertyChangeSupport = new PropertyChangeSupport(this);
        }

        propertyChangeSupport.addPropertyChangeListener(listener);
    }

    @Override
    public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener)
    {
        if (propertyChangeSupport == null)
        {
            propertyChangeSupport = new PropertyChangeSupport(this);
        }

        propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
    }

    @Override
    public void removePropertyChangeListener(PropertyChangeListener listener)
    {
        if (propertyChangeSupport != null)
        {
            propertyChangeSupport.removePropertyChangeListener(listener);
        }

    }

    @Override
    public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener)
    {
        if (propertyChangeSupport != null)
        {
            propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
        }
    }

    @Override
    public void firePropertyChange(String propertyName, Object oldValue, Object newValue)
    {
        if (propertyChangeSupport != null)
        {
            propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
        }
    }

    @Override
    public void firePropertyChange(PropertyChangeEvent evt)
    {
        if (propertyChangeSupport != null)
        {
            propertyChangeSupport.firePropertyChange(evt);
        }
    }

    @Override
    public boolean hasListeners(String propertyName)
    {
        if (propertyChangeSupport == null)
        {
            return false;
        }

        return propertyChangeSupport.hasListeners(propertyName);
    }

    @Override
    public int count()
    {
        return propertiesMap.size();
    }

    @Override
    public boolean isEmpty()
    {
        return count() == 0;
    }

    @Override
    public Object clone() throws CloneNotSupportedException
    {
        try
        {
            return DeepCopy.deepCopy(this);
        }
        catch (Exception exc)
        {
            throw new CloneNotSupportedException(exc.getMessage());
        }
    }

    @Override
    public String toString()
    {
        StringBuilder sb = new StringBuilder("[ ");
        for (String key : propertiesMap.keySet())
        {
            sb.append(key).append(", ");
        }
        sb.append(" ]");
        return sb.toString();
    }

    @Override
    public Iterator<DynamicProperty> iterator()
    {
        return propertiesMap.values().iterator();
    }
}
